﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;

namespace FSModEd
{
    class POF
    {
		public POF() 
		{
		
		}

        public class Header 
        {
            public char[] file_id;
            public int version;

			public Header() 
			{
				file_id = "PSPO".ToArray<char>();
				version = 2117;
			}
        }

		Header head;

        #region Custom data structures
		public class vector 
        {
            public float X;
            public float Y;
            public float Z;

            public vector(float x, float y, float z) 
            {
                X = x;
                Y = y;
                Z = z;
            }

			public vector() 
			{
				X = Y = Z = 0f;
			}

			public static vector Read(ref BinaryReader reader) 
			{
				vector v = new vector();
				v.X = reader.ReadSingle();
				v.Y = reader.ReadSingle();
				v.Z = reader.ReadSingle();

				return v;
			}

			public void Write(ref BinaryWriter writer) 
			{
				writer.Write(X);
				writer.Write(Y);
				writer.Write(Z);
			}

			public static int size = sizeof(float) * 3;
        }

		class cross_section 
        {
            public float depth;
            public float radius;

			public void Read(ref BinaryReader reader) 
			{
				depth = reader.ReadSingle();
				radius = reader.ReadSingle();
			}

			public void Write(ref BinaryWriter writer) 
			{
				writer.Write(depth);
				writer.Write(radius);
			}

			public cross_section() 
			{
				depth = radius = 0f;
			}

			public static int size = sizeof(float) + sizeof(float);
        }

		class light 
        {
            public vector location;
            public int light_type;

			public light() 
			{
				location = new vector();
				light_type = 0;
			}

			public void Read(ref BinaryReader reader) 
			{
				location = vector.Read(ref reader);
				light_type = reader.ReadInt32();
			}

			public void Write(ref BinaryWriter writer) 
			{
				location.Write(ref writer);
				writer.Write(light_type);
			}

			public static int size = sizeof(int) + vector.size;
        }

		public struct path
        {
            public string name;
            public string parent;
            public struct vert 
            {
                public vector pos;
                public float radius;
                public int sobj_number;
				public int[] turret_ids;
            }

            public vert[] verts;
        }

		struct special_point 
        {
            public string name;
            public string properties;
            public vector point;
            public float radius;
        }

		struct shldface 
        {
            public vector face_normal;
            public int[] face_vertices; //Index into vertices array
            public int[] neighbors;     //Index into faces array
        }

		struct eye_position 
		{
			public int sobj_number;
			public vector sobj_offset;
			public vector normal;
		}

		public struct gun 
		{
			public vector point;
			public vector norm;
		}

		public struct slot 
		{
			public gun[] guns;
		}

		public struct bank 
		{
			public int sobj_parent;
			public int sobj_parent_phys;

			public vector turret_normal;
			public vector[] firing_points;
		}

		public struct dock 
		{
			public string properties;
			public int[] spline_path_numbers; // Index into model paths (SEE: PATH chunk)
		}

		public struct dockpoint 
		{
			public vector position;
			public vector normal;
		}

		public struct glow 
		{
			public vector pos;
			public vector norm;
			public float radius;
		}

		public struct thruster 
		{
			public string properties;
			public glow[] glows;
		}

		public struct insignia 
		{
			public int detail_level;
			public vector[] vertex_positions;
			public vector offset;

			public struct face 
			{
				public int vertex_index;
				public float u_texture_coordinate;
				public float v_texture_coordinate;
			}

			public face[] faces;
		}

		public struct sldcnode 
		{
			public byte type;
			public uint size;
				
			public vector bound_min;
			public vector bound_max;

			//For type 0 (SPLIT) nodes
			uint front_offset;
			uint back_offset;

			//For type 1 (LEAF) nodes
			uint[] polygons; //Index into shield mesh face list
		}

		public struct glowpoint
		{
			public vector point;
			public vector norm;
			public float radius;
		}

		public struct glowbank
		{
			public int disp_time;
			public int on_time;
			public int off_time;
			public int obj_parent;
			public int LOD;
			public int type;

			public glowpoint[] glowpoints;
		}
		#endregion

		#region Chunks
		public abstract class Chunk
		{
			public char[] chunk_id;
			public int size;

			public abstract void Read(ref BinaryReader reader);
			public abstract void Write(ref BinaryWriter writer);

			public void WriteHeader(ref BinaryWriter writer) 
			{
				writer.Write(chunk_id);
				writer.Write(size);
			}
		}

        public class HDR2 : Chunk 
        {
            float max_radius = 0;           // maximum radius of entire ship
			Int32 obj_flags = 0;              // object flags. Bit 1 = Textures contain tiling
			Int32 num_subobjects = 0;         // number of subobjects

            vector min_bounding = new vector();        // min bounding box point
            vector max_bounding = new vector();        // max bounding box point

			Int32[] detail_levels;        // Subobject IDs for LOD0 - LOD3
			Int32[] debris_pieces;        // Subobject IDs for Debris models

            float mass = 0;
            vector mass_center = new vector();
            vector[] moment_inertia = new vector[3];

            cross_section[] cross_sections;

			light[] lights;
            
            public HDR2() 
            {
                chunk_id = "HDR2".ToArray<char>();
            }

			public override void Read(ref BinaryReader reader)
			{
				max_radius = reader.ReadSingle();
				obj_flags = reader.ReadInt32();
				num_subobjects = reader.ReadInt32();

				min_bounding = vector.Read(ref reader);
				max_bounding = vector.Read(ref reader);

				int num_lods = reader.ReadInt32();
				detail_levels = new Int32[num_lods];
				for (int i = 0; i < num_lods; ++i) 
				{
					detail_levels[i] = reader.ReadInt32();
				}

				int num_debris = reader.ReadInt32();
				if (num_debris > 0)
				{
					debris_pieces = new Int32[num_debris];
					for (int i = 0; i < num_debris; ++i)
					{
						debris_pieces[i] = reader.ReadInt32();
					}
				}

				mass = reader.ReadSingle();
				mass_center = vector.Read(ref reader);

				foreach (vector v in moment_inertia) 
				{
					vector t = vector.Read(ref reader);
					v.X = t.X;
					v.Y = t.Y;
					v.Z = t.Z;
				}

				int num_crosssections = reader.ReadInt32();
				if (num_crosssections > 0)
				{
					cross_sections = new cross_section[num_crosssections];

					for (int i = 0; i < num_crosssections; ++i)
					{
						cross_sections[i].Read(ref reader);
					}
				}

				int num_lights = reader.ReadInt32();
				if (num_lights > 0)
				{
					lights = new light[num_lights];

					for (int i = 0; i < num_lights; ++i)
					{
						lights[i].Read(ref reader);
					}
				}

				//set the size
				size = 104; // static size;
				size += (detail_levels.Length * 4);
				size += (debris_pieces.Length * 4);
				if (cross_sections.Length > 0)
					size += (cross_sections.Length * cross_section.size);
				size += lights.Length * light.size;
			}

			public override void Write(ref BinaryWriter writer)
			{
				WriteHeader(ref writer);

				writer.Write(max_radius);
				writer.Write(obj_flags);
				writer.Write(num_subobjects);

				min_bounding.Write(ref writer);
				max_bounding.Write(ref writer);

				writer.Write(detail_levels.Length);
				foreach (int dl in detail_levels)
					writer.Write(dl);

				writer.Write(debris_pieces.Length);
				foreach (int db in debris_pieces)
					writer.Write(db);

				writer.Write(mass);
				mass_center.Write(ref writer);

				foreach (vector v in moment_inertia)
					v.Write(ref writer);

				writer.Write(cross_sections.Length);
				foreach (cross_section cs in cross_sections)
					cs.Write(ref writer);

				writer.Write(lights.Length);
				foreach (light l in lights)
					l.Write(ref writer);
			}
        }

        public class TXTR : Chunk 
        {
            string[] filename;

            public TXTR() 
            {
                chunk_id = "TXTR".ToArray<char>();
            }

			public override void Read(ref BinaryReader reader)
			{
				int n_textures = reader.ReadInt32();

				filename = new string[n_textures];

				for (int i = 0; i < n_textures; ++i) 
				{
					filename[i] = reader.ReadString();
				}

				size = 4 + (filename.Length * 4);
				for (int i = 0; i < filename.Length; i++)
				{
					size += filename[i].Length;
				}
			}

			public override void Write(ref BinaryWriter writer)
			{
				WriteHeader(ref writer);

				writer.Write(filename.Length);
				foreach (string tex in filename) 
				{
					writer.Write(tex);
				}
			}
        }

        public class PINF : Chunk 
        {
            string info;

            public PINF() 
            {
                chunk_id = "PINF".ToArray<char>();
            }

			public override void Read(ref BinaryReader reader)
			{
				info = reader.ReadString();
			}

			public override void Write(ref BinaryWriter writer)
			{
				WriteHeader(ref writer);

				writer.Write(info);
			}
        }

        public class PATH : Chunk 
        {
            path[] paths;

            public PATH() 
            {
                chunk_id = "PATH".ToArray<char>();
            }

			public override void Read(ref BinaryReader reader)
			{
				int num_paths = reader.ReadInt32();

				if (num_paths > 0) 
				{
					paths = new path[num_paths];

					for (int i = 0; i < num_paths; ++i) 
					{
						paths[i] = new path();
						paths[i].name = reader.ReadString();
						paths[i].parent = reader.ReadString();

						int num_verts = reader.ReadInt32();
						paths[i].verts = new path.vert[num_verts];

						for (int j = 0; j < num_verts; ++j) 
						{
							paths[i].verts[j] = new path.vert();
							paths[i].verts[j].pos = vector.Read(ref reader);
							paths[i].verts[j].radius = reader.ReadSingle();

							int num_turrets = reader.ReadInt32();

							if (num_turrets > 0)
							{
								paths[i].verts[j].turret_ids = new int[num_turrets];

								for (int k = 0; k < num_turrets; ++k) 
								{
									paths[i].verts[j].turret_ids[k] = reader.ReadInt32();
								}
							}	
						}
					}
				}
			}

			public override void Write(ref BinaryWriter writer)
			{
				WriteHeader(ref writer);

				writer.Write(paths != null ? paths.Length : 0);

				if (paths != null) {
					foreach (path p in paths)
					{
						writer.Write(p.name);
						writer.Write(p.parent);

						writer.Write(p.verts.Length);

						foreach (path.vert v in p.verts)
						{
							v.pos.Write(ref writer);
							writer.Write(v.radius);

							writer.Write(v.turret_ids != null ? v.turret_ids.Length : 0);

							if (v.turret_ids != null)
							{
								foreach (int n in v.turret_ids)
								{
									writer.Write(n);
								}
							}
						}
					}
				}
			}
        }

        public class SPCL : Chunk 
        {
            special_point[] special_points;

            public SPCL() 
            {
                chunk_id = "SPCL".ToArray<char>();
            }

			public override void Read(ref BinaryReader reader)
			{
				int num_specials = reader.ReadInt32();

				special_points = new special_point[num_specials];

				for (int i = 0; i < num_specials; ++i) 
				{
					special_points[i] = new special_point();
					special_points[i].name = reader.ReadString();
					special_points[i].properties = reader.ReadString();
					special_points[i].point = vector.Read(ref reader);
					special_points[i].radius = reader.ReadSingle();
				}
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
        }

        public class SHLD : Chunk 
        {
            vector[] vertices;

            shldface[] faces;

            public SHLD() 
            {
                chunk_id = "SHLD".ToArray<char>();
            }

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
        }

		public class EYE : Chunk 
		{
			eye_position[] eye_positions;

			public EYE() 
			{
				chunk_id = " EYE".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class GPNT : Chunk 
		{
			slot[] slots;

			public GPNT() 
			{
				chunk_id = "GPNT".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class MPNT : Chunk
		{
			slot[] slots;

			public MPNT() 
			{
				chunk_id = "MPNT".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}
		
		public class TGUN : Chunk 
		{
			public bank[] banks;
			
			public TGUN() 
			{
				chunk_id = "TGUN".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class TMIS : Chunk
		{
			public bank[] banks;
			
			public TMIS() 
			{
				chunk_id = "TMIS".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class DOCK : Chunk 
		{
			dock[] docks;
			dockpoint[] points;

			public DOCK() 
			{
				chunk_id = "DOCK".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class FUEL : Chunk 
		{
			thruster[] thrusters;

			public FUEL() 
			{
				chunk_id = "FUEL".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class OBJ2 : Chunk 
		{
			int submodel_number;

			float radius;
			int submodel_parent;
			vector offset;

			vector geometric_center;
			vector bounding_box_min_point;
			vector bounding_box_max_point;

			string submodel_name;
			string properties;
			int movement_type;
			int movement_axis;

			int reserved;         // must be 0
			int bsp_data_size;    // number of bytes now following

			BSP_Data bsp_data;  // contains actual polygons, etc.

			public OBJ2() 
			{
				chunk_id = "OBJ2".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				size = 84 + submodel_name.Length + properties.Length + bsp_data_size;
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class INSG : Chunk 
		{
			public insignia[] insignias;

			public INSG()
			{
				chunk_id = "INSG".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class ACEN : Chunk 
		{
			vector point;

			public ACEN() 
			{
				chunk_id = "ACEN".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class GLOW : Chunk
		{
			glowbank[] glowbanks;

			public GLOW() 
			{
				chunk_id = "GLOW".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}

		public class SLDC : Chunk 
		{
			sldcnode[] nodes;

			public SLDC() 
			{
				chunk_id = "SLDC".ToArray<char>();
			}

			public override void Read(ref BinaryReader reader)
			{
				throw new NotImplementedException();
			}

			public override void Write(ref BinaryWriter writer)
			{
				throw new NotImplementedException();
			}
		}
		#endregion

		#region BSP Data
		public abstract class BSP
		{
			/// <summary>
			/// Only used for inheritance purposes. The BSP_Data base class has no head, and does not write one.
			/// </summary>
			public BSP_BlockHeader head;

			/// <summary>
			/// Size of this chunk in bytes. Includes Header size.
			/// </summary>
			/// <returns>Size of this chunk in bytes. Includes Header size.</returns>
			public abstract int Size();

			/// <summary>
			/// Reads a section of the BSP tree
			/// </summary>
			/// <param name="reader">The Reader to use</param>
			public abstract void Read(ref BinaryReader reader);

			/// <summary>
			/// Writes a section of the BSP tree. Always writes both the section header and the section.
			/// </summary>
			/// <param name="writer">The Writer to use</param>
			public abstract void Write(ref BinaryWriter writer);

			public BSP()
			{
				head = new BSP_BlockHeader();
			}


		}
		
		public class BSP_Data : BSP
		{
			/// <summary>
			/// A collection of all sections in this BSP structure.
			/// </summary>
			public List<BSP> Sections;

			public override int Size()
			{
				int size = 0;

				foreach (BSP section in Sections)
					size += section.Size();

				return size;
			}
			
			public override void Read(ref BinaryReader reader)
			{
				bool go = true;

				while (go)
				{
					head.Read(ref reader);

					switch (head.id)
					{
						case BSP_BlockHeader.block_id.DEFPOINTS:
							Sections.Add(new BSP_DefPoints(head, ref reader));
							break;
						case BSP_BlockHeader.block_id.FLATPOLY:
							Sections.Add(new BSP_FlatPoly(head, ref reader));
							break;
						case BSP_BlockHeader.block_id.TMAPPOLY:
							Sections.Add(new BSP_TMAPPoly(head, ref reader));
							break;
						case BSP_BlockHeader.block_id.SORTNORM:
							Sections.Add(new BSP_SortNorm(head, ref reader));
							break;
						case BSP_BlockHeader.block_id.BOUNDBOX:
							Sections.Add(new BSP_BoundingBox(head, ref reader));
							break;
						case BSP_BlockHeader.block_id.EOF:
							go = false;
							break;
					}
				}
			}

			public override void Write(ref BinaryWriter writer) 
			{
				writer.Write(Size());

				foreach (BSP section in Sections) 
				{
					section.Write(ref writer);
				}
			}
		}

		public class BSP_BlockHeader
		{
			public enum block_id 
			{
				EOF = 0,
				DEFPOINTS,
				FLATPOLY,
				TMAPPOLY,
				SORTNORM,
				BOUNDBOX
			}

			public block_id id;
			public int size; //number of bytes in this structure instance - dynamic

			//---------------------------------------
			public void Read(ref BinaryReader buffer) 
			{
				id = (block_id)buffer.ReadInt32();
				size = buffer.ReadInt32();
			}

			public void Write(ref BinaryWriter buffer) 
			{
				buffer.Write((int)id);
				buffer.Write(size);
			}

			public BSP_BlockHeader(block_id _id, int _size) 
			{
				id = _id;
				size = _size;
			}

			public BSP_BlockHeader() 
			{
				id = block_id.EOF;
				size = 0;
			}

			public int Size() 
			{
				return 8;
			}
		}

		public class BSP_DefPoints : BSP 
		{
			struct vertdata 
			{
				public vector position;
				public List<vector> normals;
			}

			Int32 n_verts;
			Int32 n_norms;
			Int32 offset;
			List<byte> normcounts;		//Should always be n_verts in length
			List<vertdata> vertices;

			public override void Read(ref BinaryReader reader) 
			{
				long baseOffset = reader.BaseStream.Position;
				n_verts = reader.ReadInt32();
				n_norms = reader.ReadInt32();
				offset = reader.ReadInt32();

				normcounts = new List<byte>();

				for (int i = 0; i < n_verts; ++i) 
				{
					normcounts.Add(reader.ReadByte());
				}

				reader.BaseStream.Position = baseOffset + offset;

				vertices = new List<vertdata>();

				for (int i = 0; i < n_verts; ++i) 
				{
					vertdata vertex = new vertdata();

					vertex.position = vector.Read(ref reader);

					if (normcounts[i] > 0) 
					{
						vertex.normals = new List<vector>();

						for (int j = 0; j < normcounts[i]; ++j) 
						{
							vertex.normals.Add(vector.Read(ref reader));
						}
					}
				}	
			}

			public override void Write(ref BinaryWriter writer) 
			{
				head.size = Size();
				head.Write(ref writer);

				writer.Write(n_verts);
				writer.Write(n_norms);
				writer.Write(offset);

				for (int i = 0; i < n_verts; ++i) 
				{
					writer.Write(normcounts[i]);
				}

				for (int i = 0; i < n_verts; ++i) 
				{
					vertices[i].position.Write(ref writer);

					foreach(vector norm in vertices[i].normals) 
					{
						norm.Write(ref writer);
					}
				}
			}

			public BSP_DefPoints(BSP_BlockHeader _head) 
			{
				head = _head;
				n_verts = 0;
				n_norms = 0;
				offset = 0;
			}

			public BSP_DefPoints(BSP_BlockHeader _head, ref BinaryReader reader) 
			{
				head = _head;
				n_verts = 0;
				n_norms = 0;
				offset = 0;

				Read(ref reader);
			}

			public override int Size() 
			{
				int collector = 0;

				for (int i = 0; i < n_verts; ++i) 
				{
					collector += normcounts[i];
				}

				return 20 + n_verts + vector.size  * (collector + n_verts);
			}
		}

		public class BSP_FlatPoly : BSP
		{
			public class flat_vertex
			{
				public ushort vertnum = 0;
				public ushort normnum = 0;

				public void Read(ref BinaryReader reader) 
				{
					vertnum = reader.ReadUInt16();
					normnum = reader.ReadUInt16();
				}

				public void Write(ref BinaryWriter writer) 
				{
					writer.Write(vertnum);
					writer.Write(normnum);
				}

				public static int Size() 
				{
					return 4;
				}

			}

			vector normal = new vector();
			vector center = new vector();
			float radius = 0f;
			Int32 nverts = 0;

			byte red = 0, green = 0, blue = 0, pad = 0;

			List<flat_vertex> verts = new List<flat_vertex>();

			public override void Read(ref BinaryReader reader) 
			{
				normal = vector.Read(ref reader);
				center = vector.Read(ref reader);

				radius = reader.ReadSingle();
				nverts = reader.ReadInt32();

				red = reader.ReadByte();
				green = reader.ReadByte();
				blue = reader.ReadByte();
				pad = reader.ReadByte();

				for (int i = 0; i < nverts; ++i) 
				{
					flat_vertex vert = new flat_vertex();
					vert.Read(ref reader);

					verts.Add(vert);
				}
			}

			public override void Write(ref BinaryWriter writer) 
			{
				head.size = Size();
				head.Write(ref writer);

				normal.Write(ref writer);
				center.Write(ref writer);

				writer.Write(radius);
				writer.Write(nverts);

				writer.Write(red);
				writer.Write(green);
				writer.Write(blue);
				writer.Write(pad);

				for (int i = 0; i < nverts; ++i) 
				{
					verts[i].Write(ref writer);
				}

			}

			public BSP_FlatPoly(BSP_BlockHeader _head) 
			{
				head = _head;
			}

			public BSP_FlatPoly(BSP_BlockHeader _head, ref BinaryReader reader)
			{
				head = _head;

				Read(ref reader);
			}

			public override int Size() 
			{
				return 44 + (flat_vertex.Size() * nverts);
			}
		}

		public class BSP_TMAPPoly : BSP 
		{
			public class tmap_vertex
			{
				ushort vertnum; //these are indices into the vertex arrays
				ushort normnum;
				float u;
				float v;

				public void Read(ref BinaryReader reader) 
				{
					vertnum = reader.ReadUInt16();
					normnum = reader.ReadUInt16();

					u = reader.ReadSingle();
					v = reader.ReadSingle();
				}

				public void Write(ref BinaryWriter writer) 
				{
					writer.Write(vertnum);
					writer.Write(normnum);
					
					writer.Write(u);
					writer.Write(v);
				}

				public static int Size() 
				{
					return 12;
				}
			}

			vector normal = new vector();
			vector center = new vector();
			float radius = 0f;
			Int32 nverts = 0;
			Int32 tmap_num = 0;
			List<tmap_vertex> verts = new List<tmap_vertex>();

			public override void Read(ref BinaryReader reader) 
			{
				normal = vector.Read(ref reader);
				center = vector.Read(ref reader);

				radius = reader.ReadSingle();
				nverts = reader.ReadInt32();
				tmap_num = reader.ReadInt32();

				for (int i = 0; i < nverts; ++i) 
				{
					tmap_vertex v = new tmap_vertex();

					v.Read(ref reader);
					verts.Add(v);
				}
			}

			public override void Write(ref BinaryWriter writer) 
			{
				head.size = Size();
				head.Write(ref writer);

				normal.Write(ref writer);
				center.Write(ref writer);

				writer.Write(radius);
				writer.Write(nverts);
				writer.Write(tmap_num);

				foreach(tmap_vertex v in verts) 
				{
					v.Write(ref writer);
				}

			}

			public BSP_TMAPPoly(BSP_BlockHeader _head) 
			{
				head = _head;
			}

			public BSP_TMAPPoly(BSP_BlockHeader _head, ref BinaryReader reader)
			{
				head = _head;

				Read(ref reader);
			}

			public override int Size()
			{
				return 44 + (tmap_vertex.Size() * nverts);
			}
		}

		public class BSP_SortNorm : BSP 
		{
			vector plane_normal = new vector();				//12	|20
			vector plane_point = new vector();				//12	|32
			Int32 reserved = 0;								//4		|36 set to 0
			Int32 front_offset = 0;							//4		|40 Only recurse into this if non-zero.
			Int32 back_offset = 0;							//4		|44 Only recurse into this if non-zero.
			Int32 prelist_offset = 0;						//4		|48 Only recurse into this if non-zero.
			Int32 postlist_offset = 0;						//4		|52 Only recurse into this if non-zero.
			Int32 online_offset = 0;						//4		|56 Only recurse into this if non-zero.
			vector min_bounding_box_point = new vector();	//12	|68 of all polys under here
			vector max_bounding_box_point = new vector();	//12	|80 of all polys under here 

			public override void Read(ref BinaryReader reader) 
			{
				plane_normal = vector.Read(ref reader);
				plane_point = vector.Read(ref reader);

				reserved = reader.ReadInt32();
				front_offset = reader.ReadInt32();
				back_offset = reader.ReadInt32();
				prelist_offset = reader.ReadInt32();
				postlist_offset = reader.ReadInt32();
				online_offset = reader.ReadInt32();

				min_bounding_box_point = vector.Read(ref reader);
				max_bounding_box_point = vector.Read(ref reader);
			}

			public override void Write(ref BinaryWriter writer)
			{
				head.size = Size();
				head.Write(ref writer);
				
				plane_normal.Write(ref writer);
				plane_point.Write(ref writer);

				writer.Write(reserved);
				writer.Write(front_offset);
				writer.Write(back_offset);
				writer.Write(prelist_offset);
				writer.Write(postlist_offset);
				writer.Write(online_offset);

				min_bounding_box_point.Write(ref writer);
				max_bounding_box_point.Write(ref writer);
			}

			public BSP_SortNorm(BSP_BlockHeader _head) 
			{
				head = _head;
			}

			public BSP_SortNorm(BSP_BlockHeader _head, ref BinaryReader reader)
			{
				head = _head;

				Read(ref reader);
			}

			public override int Size()
			{
				return 80;
			}
		}

		public class BSP_BoundingBox : BSP 
		{
			vector min = new vector();
			vector max = new vector();

			public override void Read(ref BinaryReader reader) 
			{
				min = vector.Read(ref reader);
				max = vector.Read(ref reader);
			}

			public override void Write(ref BinaryWriter writer)
			{
				head.size = Size();
				head.Write(ref writer);
				min.Write(ref writer);
				max.Write(ref writer);
			}

			public BSP_BoundingBox(BSP_BlockHeader _head) 
			{
				head = _head;
			}

			public BSP_BoundingBox(BSP_BlockHeader _head, ref BinaryReader reader)
			{
				head = _head;

				Read(ref reader);
			}

			public override int Size()
			{
				return 32;
			}
		}
		#endregion
	}
}
